using CUE4Parse.UE4.Objects.Core.Misc;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;
using FModel.ViewModels.ApiEndpoints.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Collections.ObjectModel;

namespace FModel.ViewModels;

public class GameEntry
{
    public string Name { get; set; }
    public string Key { get; set; }
    public string GamePath { get; set; }
    public override string ToString() => $"{Name} - {Key}";
}

public class AesManagerViewModel : ViewModel
{
    private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView;

    public const string AES_KEYS_FILE = "AesKeys.json";

    public FullyObservableCollection<FileItem> AesKeys { get; private set; }
    public ICollectionView AesKeysView { get; private set; }
    public bool HasChange { get; set; }

    private ObservableCollection<GameEntry> _gameEntries;
    public ObservableCollection<GameEntry> GameEntries
    {
        get => _gameEntries;
        set
        {
            if (SetProperty(ref _gameEntries, value))
            {
                UpdateFilteredGameEntries();
            }
        }
    }

    private ObservableCollection<GameEntry> _filteredGameEntries;
    public ObservableCollection<GameEntry> FilteredGameEntries
    {
        get => _filteredGameEntries;
        set => SetProperty(ref _filteredGameEntries, value);
    }

    private string _gameFilterText = string.Empty;
    public string GameFilterText
    {
        get => _gameFilterText;
        set
        {
            if (SetProperty(ref _gameFilterText, value))
            {
                UpdateFilteredGameEntries();
            }
        }
    }

    private string _selectedGameKey;
    public string SelectedGameKey
    {
        get => _selectedGameKey;
        set
        {
            if (SetProperty(ref _selectedGameKey, value) && !string.IsNullOrEmpty(value))
            {
                ApplySelectedGameKey(value);
            }
        }
    }

    private AesResponse _keysFromSettings;
    private HashSet<FGuid> _uniqueGuids;
    private readonly CUE4ParseViewModel _cue4Parse;
    private readonly FileItem _mainKey = new("主静态密钥", 0) { Guid = Constants.ZERO_GUID };

    public AesManagerViewModel(CUE4ParseViewModel cue4Parse)
    {
        _cue4Parse = cue4Parse;
        HasChange = false;
        LoadGameEntries();
        FilteredGameEntries = new ObservableCollection<GameEntry>();
        UpdateFilteredGameEntries();
    }

    private void LoadGameEntries()
    {
        var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AES_KEYS_FILE);
        if (File.Exists(filePath))
        {
            try
            {
                var json = File.ReadAllText(filePath);
                var gameList = JsonConvert.DeserializeObject<List<GameEntry>>(json) ?? new List<GameEntry>();
                GameEntries = new ObservableCollection<GameEntry>(gameList);
            }
            catch
            {
                GameEntries = new ObservableCollection<GameEntry>();
            }
        }
        else
        {
            GameEntries = new ObservableCollection<GameEntry>();
        }
    }

    private void SaveGameEntries()
    {
        var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AES_KEYS_FILE);
        var json = JsonConvert.SerializeObject(GameEntries.ToList(), Formatting.Indented);
        File.WriteAllText(filePath, json);
    }

    private void UpdateFilteredGameEntries()
    {
        if (GameEntries == null)
        {
            FilteredGameEntries = new ObservableCollection<GameEntry>();
            return;
        }

        var sortedAndFiltered = GameEntries
            .Where(g => string.IsNullOrEmpty(GameFilterText) ||
                       (g.Name?.Contains(GameFilterText, StringComparison.OrdinalIgnoreCase) == true))
            .OrderBy(g => g.Name)
            .ToList();

        FilteredGameEntries = new ObservableCollection<GameEntry>(sortedAndFiltered);

        if (FilteredGameEntries.Count == 1)
        {
            SelectedGameKey = FilteredGameEntries[0].Key;
        }
    }

    public void AddGameEntry(string name, string key, string gamePath = null)
    {
        if (!key.StartsWith("0x"))
        {
            key = "0x" + key;
        }

        var entry = new GameEntry { Name = name, Key = key, GamePath = gamePath };
        GameEntries.Add(entry);
        SaveGameEntries();
        UpdateFilteredGameEntries();

        SelectedGameKey = key;
    }

    public void RemoveGameEntry(GameEntry entry)
    {
        GameEntries.Remove(entry);
        SaveGameEntries();
        UpdateFilteredGameEntries();
    }

    private void ApplySelectedGameKey(string key)
    {
        if (AesKeys != null && AesKeys.Count > 0)
        {
            AesKeys[0].Key = key;
            HasChange = true;

            if (!string.IsNullOrEmpty(_keysFromSettings.MainKey))
            {
                _keysFromSettings.MainKey = key;
            }
        }
    }

    public async Task InitAes()
    {
        await _threadWorkerView.Begin(_ =>
        {
            _keysFromSettings = UserSettings.Default.CurrentDir.AesKeys;
            _mainKey.Key = Helper.FixKey(_keysFromSettings.MainKey);
            AesKeys = new FullyObservableCollection<FileItem>(EnumerateAesKeys());
            AesKeys.ItemPropertyChanged += AesKeysOnItemPropertyChanged;
            AesKeysView = new ListCollectionView(AesKeys) { SortDescriptions = { new SortDescription("名称", ListSortDirection.Ascending) } };
        });
    }

    private void AesKeysOnItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
    {
        if (e.PropertyName != "Key" || sender is not FullyObservableCollection<FileItem> collection)
            return;

        var key = Helper.FixKey(collection[e.CollectionIndex].Key);
        if (e.CollectionIndex == 0)
        {
            if (!HasChange)
                HasChange = Helper.FixKey(_keysFromSettings.MainKey) != key;

            _keysFromSettings.MainKey = key;
        }
        else if (!_keysFromSettings.HasDynamicKeys)
        {
            HasChange = true;
            _keysFromSettings.DynamicKeys = new List<DynamicKey>
            {
                new()
                {
                    Key = key,
                    Name = collection[e.CollectionIndex].Name,
                    Guid = collection[e.CollectionIndex].Guid.ToString()
                }
            };
        }
        else if (_keysFromSettings.DynamicKeys.FirstOrDefault(x => x.Guid == collection[e.CollectionIndex].Guid.ToString()) is { } d)
        {
            if (!HasChange)
                HasChange = Helper.FixKey(d.Key) != key;

            d.Key = key;
        }
        else
        {
            HasChange = true;
            _keysFromSettings.DynamicKeys.Add(new DynamicKey
            {
                Key = key,
                Name = collection[e.CollectionIndex].Name,
                Guid = collection[e.CollectionIndex].Guid.ToString()
            });
        }
    }
    public void UpdateGameEntry(GameEntry gameToUpdate, string newName, string newKey, string newGamePath = null)
    {
        if (!newKey.StartsWith("0x"))
        {
            newKey = "0x" + newKey;
        }

        gameToUpdate.Name = newName;
        gameToUpdate.Key = newKey;
        gameToUpdate.GamePath = newGamePath;

        SaveGameEntries();
        UpdateFilteredGameEntries();

        if (SelectedGameKey == gameToUpdate.Key)
        {
            SelectedGameKey = newKey;
        }
    }
    public void SetAesKeys()
    {
        UserSettings.Default.CurrentDir.AesKeys = _keysFromSettings;
    }

    private IEnumerable<FileItem> EnumerateAesKeys()
    {
        yield return _mainKey;
        _uniqueGuids = new HashSet<FGuid> { Constants.ZERO_GUID };

        var hasDynamicKeys = _keysFromSettings.HasDynamicKeys;
        foreach (var file in _cue4Parse.GameDirectory.DirectoryFiles)
        {
            if (file.Guid == Constants.ZERO_GUID || !_uniqueGuids.Add(file.Guid))
                continue;

            var k = string.Empty;
            if (hasDynamicKeys && _keysFromSettings.DynamicKeys.FirstOrDefault(x => x.Guid == file.Guid.ToString()) is { } dynamicKey)
            {
                k = dynamicKey.Key;
            }

            file.Key = Helper.FixKey(k);
            yield return file;
        }
    }
}